#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>

#include <linux/of_gpio.h>

#include <linux/cdev.h>
#include <linux/device.h>

#include <linux/atomic.h>

#include <linux/ide.h>

#define KEY0VALUE   0xF0
#define INVAKEY     0x00

struct ST_KEY{

    //设备节点
    struct device_node *pst_key_node;
    //GPIO的id
    int key_gpio_id;

    //申请设备号
    dev_t devid;
    int major;
    int minor;
    //添加字符设备
    struct cdev st_cdev;

    //创建设备节点
    struct class *pst_class;
    struct device *pst_device;

    //
    atomic_t keyvalue;
};

struct ST_KEY st_key;

int KeyInit(void)
{
    //查找keygpio的设备节点static inline struct device_node *of_find_node_by_path(const char *path)
    st_key.pst_key_node = of_find_node_by_path("/lux_key");
    if (st_key.pst_key_node == NULL)
    {
        printk("Node lux_key not find\r\n");
        return -1;
    }
    
    //根据节点查找个gpio的id
    /*
    static inline int of_get_named_gpio(struct device_node *np,
                                   const char *propname, int index)
    */
    st_key.key_gpio_id = of_get_named_gpio(st_key.pst_key_node,"key-gpio",0);
    if (st_key.key_gpio_id < 0)
    {
        printk("not find gpio id\r\n");
        return -1;
    }

    //static inline int gpio_request(unsigned gpio, const char *label)
    gpio_request(st_key.key_gpio_id,"key-gpio");
    gpio_direction_input(st_key.key_gpio_id);

    return 0;
}

int key_open(struct inode *nd, struct file *fp)
{
    int ret_val;

    ret_val = KeyInit();
    if(ret_val < 0)
    {
        printk("KeyInit failed\r\n");
        return -1;
    }
    fp->private_data = &st_key;
    return 0;
}

ssize_t key_read(struct file *fp, char __user *buf, size_t len, loff_t *offset)
{

    unsigned char value;
    int ret_val = 0;
    struct ST_KEY *st_private = fp->private_data;

    if (gpio_get_value(st_private->key_gpio_id) == 0)//按键按下
    {
        while (!gpio_get_value(st_private->key_gpio_id));//等待按键释放
        atomic_set(&st_private->keyvalue,KEY0VALUE);
    }
    else
    {
        atomic_set(&st_private->keyvalue,INVAKEY);
    }
    value = atomic_read(&st_private->keyvalue);
    
    ret_val = copy_to_user(buf,&value,sizeof(value));
    return ret_val;
}

struct file_operations key_fops = 
{
    .owner = THIS_MODULE,
    .open = key_open,
    .read = key_read,
};

static int __init key_init_lux(void)
{

    atomic_set(&st_key.keyvalue,INVAKEY);
    
    //申请设备号int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
    alloc_chrdev_region(&st_key.devid,0,1,"dtskey");
    st_key.major = MAJOR(st_key.devid);
    st_key.minor = MINOR(st_key.devid);
    printk("major:%d\tminor:%d\r\n",st_key.major,st_key.minor);

    //向 内核 添加字符设备
    st_key.st_cdev.owner = THIS_MODULE;
    //void cdev_init(struct cdev *, const struct file_operations *);
    cdev_init(&st_key.st_cdev,&key_fops);
    //int cdev_add(struct cdev *, dev_t, unsigned);
    cdev_add(&st_key.st_cdev,st_key.devid,1);

    //创建设备节点
    st_key.pst_class = class_create(THIS_MODULE,"dtskey");
    /*
    struct device *device_create(struct class *cls, struct device *parent,
			     dev_t devt, void *drvdata,
			     const char *fmt, ...);
    */
    st_key.pst_device = device_create(st_key.pst_class,NULL,st_key.devid,NULL,"chrdev_key");
    return 0;
}


static void __exit key_exit_lux(void)
{
    unregister_chrdev_region(st_key.devid,1);
    gpio_free(st_key.key_gpio_id);
    cdev_del(&st_key.st_cdev);
    device_destroy(st_key.pst_class,st_key.devid);
    class_destroy(st_key.pst_class);
}

module_init(key_init_lux);
module_exit(key_exit_lux);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lux");